#include "TestScene.h"

#include "StaticModel.h"
#include "SkyDome.h"

//Adv models
#include "OceanAdvModel.h"
#include "InlandWaterAdvModel.h"
#include "Terrain.h"

TestScene::TestScene()
{
	mod = 0;
	objMod = 0;
	asimpMod = 0;
	loadedMod = 0;
	teapotMod = 0;

	iMod = 0;
	iObjMod = 0;
	iAsimpMod = 0;

	animMod = 0;

	skyDome = 0;

	font = 0;
	spriteBatch = 0;
	tex2D = 0;

	//adv model
	ocean = 0;
	inlandWater = 0;

	//Terrain
	terrain = 0;
}

TestScene::~TestScene()
{}

void TestScene::OpenScene()
{
	//Seed random number generator to 0 - thus, each time we run the game, the 
	//point/spot lights will be located and coloured exactly the same. 
	srand(0);

	skyDome = new SkyDome(sharedGraphicsInterface->GetDevice(),
		L"Terrain/QuantumTheory/QuantumHighlands/Textures/SkyDome/cubeMap.dds", 
		//L"Textures/Cloud_CubeMap.dds",
		camera);
	sharedGraphicsInterface->SetCurrentSkyDome(skyDome);
	//sharedGraphicsInterface->SetCurrentSkyDome(NULL);

	//Create terrain 
	terrain = new Terrain();
	terrain->Init(sharedGraphicsInterface->GetDevice(), skyDome);

	//Disable MSAA
	sharedGraphicsInterface->Set4XMSAA(false);

	//Init camera position / frustum properties.
	camera->SetPosition(XMFLOAT3(0.0f, 120.0f, 0.0f));
	camera->RebuildViewMatrix();
	OnResize();

	////Load animating model
	//animMod = new AnimatedModel();
	//animMod->CreateAnimatedModelThroughAssimp(sharedGraphicsInterface->GetDevice(), 
	//	std::string("AnimModels/boblampclean.md5mesh"), std::string("AnimModels/"), 
	//	false, true, false, false);
	//animMod->SetWorldMatrix(
	//	XMFLOAT3(20.0f, terrain->GetHeightAtWorldSpacePosition(20.0f, 300.0f), 300.0f), 
	//	XMFLOAT3(MathHelper::ToRadians(-90.0), MathHelper::ToRadians(180.0f), 0.0f),
	//	XMFLOAT3(0.04f, 0.04f, 0.04f));
	//Material materialForAnimMod;
	//materialForAnimMod.Diffuse.w = 1.0f;
	//materialForAnimMod.Specular = XMFLOAT4(0,0,0,1);
	//materialForAnimMod.Ambient = XMFLOAT4(0.2f, 0.2f, 0.2f, 1.0f);
	//animMod->SetSubmodelsWithSameMaterial(materialForAnimMod);

	////Load model through assimp.
	//asimpMod = new StaticModel();
	//asimpMod->CreateModelThroughAssimp(sharedGraphicsInterface->GetDevice(), 
	//	std::string("Models/cup.obj"), std::string("Textures/"), false, false, false, false, false);
	//asimpMod->SetWorldMatrix(XMFLOAT3(10.0f, 1.0f, 20.0f), 
	//	XMFLOAT3(0.0f, 0.0f, 0.0f), XMFLOAT3(1.0f, 1.0f, 1.0f));

	////And another...
	//loadedMod = new StaticModel();
	//loadedMod->CreateModelThroughAssimp(sharedGraphicsInterface->GetDevice(), 
	//	std::string("Models/tree_oak.obj"), std::string("Textures/"), true, true, true, false, false);
	//loadedMod->SetWorldMatrix(XMFLOAT3(5.0f, 0.0f, 5.0f), 
	//	XMFLOAT3(0.0f, 0.0f, 0.0f), XMFLOAT3(0.5f, 0.5f, 0.5f));

	////Load (instanced) model through assimp
	//iAsimpMod = new StaticInstancedModel(sharedGraphicsInterface->GetDevice(), 3);
	//iAsimpMod->CreateModelThroughAssimp(sharedGraphicsInterface->GetDevice(), 
	//	std::string("Models/cup.obj"), std::string("Textures/"), false, false, false, false, false);
	//iAsimpMod->ChangeMaximumInstances(sharedGraphicsInterface->GetDevice(), 5);

	////Create instance data for the instanced model created through assimp.
	//for (unsigned i = 0; i < 5; i++)
	//{
	//	XMFLOAT3 p(i*2.0f+5.0f, 1.0f, 25.0f);
	//	XMFLOAT3 r(0.0f, 0.0f, 0.0f);
	//	XMFLOAT3 s(1.0f, 1.0f, 1.0f);
	//	iAsimpMod->CreateInstance(p, r, s, true);
	//}

	//Material description structure for Grid and (Instanced) Box.
	StaticModelBasicMaterial m;
	m.UseTextureMap = true;
	m.UseNormalMap = true;
	m.UseDisplacementMap = false;
	m.UseSeperateHeightMap = false;
	m.TextureMapFilePath = L"Textures/bricks.dds";
	m.NormalMapFilePath =  L"Textures/bricks_nmap.dds";
	m.HeightMapFilePath =  L"Textures/Samp/bricks_05_hm.png";
	m.IsOpaque = true;
	m.Material.Diffuse.w = 0.5f;
	m.Material.Ambient = XMFLOAT4(0.2f, 0.2f, 0.2f, 1.0f);
	m.Material.Specular = XMFLOAT4(0.2f, 0.3f, 0.2f, 128.0f);
	//m.Material.Reflection = XMFLOAT4(0.7f, 0.7f, 0.7f, 1.0f);
	m.TexScale = XMFLOAT2(15, 15);

	//Grid - TM/NM/DM
	mod = new StaticModel();
	//mod->CreateGrid(30, 30, 5, 5, &m, sharedGraphicsInterface->GetDevice());
	//mod->CreateBox(0.5f, 0.5f, 0.5f, &m, sharedGraphicsInterface->GetDevice());
	mod->CreateSphere(0.15f, 10, 10, &m, sharedGraphicsInterface->GetDevice());
	mod->SetWorldPosition(XMFLOAT3(20.0f, terrain->GetHeight(20.0f, 300.0f)+0.15f, 300.0f));
	mod->SetWorldRotation(XMFLOAT3(0.0f, 0.0f, 0.0f));
	mod->SetWorldScale(XMFLOAT3(1.0f, 1.0f, 1.0f));

	////OBJ Cup.
	//objMod = new StaticModel();
	//std::vector<LPCWSTR>mats(1);
	//mats[0] = L"Models/cup.mtl";
	//objMod->CreateModelFromOBJFile(sharedGraphicsInterface->GetDevice(),
	//	L"Models/cup.obj", mats, "Textures/",false, NULL);
	//objMod->SetWorldPosition(XMFLOAT3(3.0f, 0.8f, 5.0f));
	//objMod->SetWorldRotation(XMFLOAT3(0.0f, 0.0f, 0.0f));
	//objMod->SetWorldScale(XMFLOAT3(1.0f, 1.0f, 1.0f));

	////Teapot
	//Material teapotMaterial;
	//teapotMaterial.Specular = XMFLOAT4(0.0f, 0.0f, 0.0f, 1.0f);
	//teapotMaterial.Diffuse = XMFLOAT4(0.2f, 0.2f, 0.2f, 1.0f);
	//teapotMaterial.Ambient = XMFLOAT4(0.2f, 0.2f, 0.2f, 1.0f);
	//teapotMod = new StaticModel();
	//teapotMod->CreateModelThroughAssimp(sharedGraphicsInterface->GetDevice(), 
	//	std::string("Teapot/teapot.obj"), std::string("Teapot/"), true, true, true, false, false);
	//teapotMod->SetWorldPosition(XMFLOAT3(6.3f, 3.0f, 1.0f));
	//teapotMod->SetWorldScale(XMFLOAT3(0.01f, 0.01f, 0.01f));
	//teapotMod->SetSubmodelsWithSameMaterial(teapotMaterial);
	//teapotMod->SetSubmodelsWithSameReflectionData(XMFLOAT3(0.45f, 0.45f, 0.45f));

	////Instanced box - TM/NM/DM
	//iMod = new StaticInstancedModel(sharedGraphicsInterface->GetDevice(), 5);
	//iMod->CreateBox(1, 1, 1, &m, sharedGraphicsInterface->GetDevice());
	//for (unsigned i = 0; i < 5; i++)
	//{
	//	XMFLOAT3 pos = XMFLOAT3(i*2.0f+5.0f, 0.5f, 10.0f);
	//	XMFLOAT3 ident = XMFLOAT3(1.0f, 1.0f, 1.0f);
	//	XMFLOAT3 zero = XMFLOAT3(0.0f, 0.0f, 0.0f);
	//	iMod->CreateInstance(pos, zero, ident, true);
	//}

	////Instanced OBJ Cup
	//iObjMod = new StaticInstancedModel(sharedGraphicsInterface->GetDevice(), 5);
	//iObjMod->CreateModelFromOBJFile(sharedGraphicsInterface->GetDevice(),
	//	L"Models/cup.obj", mats, "Textures/",false, NULL);
	//for (unsigned i = 0; i < 5; i++)
	//{
	//	XMFLOAT3 pos = XMFLOAT3(i*2.0f+5.0f, 0.7f, 15.0f);
	//	XMFLOAT3 ident = XMFLOAT3(1.0f, 1.0f, 1.0f);
	//	XMFLOAT3 zero = XMFLOAT3(0.0f, 0.0f, 0.0f);
	//	iObjMod->CreateInstance(pos, zero, ident, true);
	//}

	//Set scene lighting. Randomly place lights in a grid: 40x10x60
	//Done using new to avoid stack overflow issues when we have > 3000 lights
	//or so.
	TileBasedLight* lightArr = new TileBasedLight[NUM_LIGHTS];
	//Loop through lights and set properties.
	for (UINT i = 0; i < NUM_LIGHTS; i++)
	{
		//No colour output. 
		XMFLOAT4 null = XMFLOAT4(0.0f, 0.0f, 0.0f, 1.0f);
		//Random diffuse and spec colour.
		XMFLOAT4 diffSpecCol = XMFLOAT4(MathHelper::RandF(), MathHelper::RandF(), 
			MathHelper::RandF(), 1.0f);
		//Ambient term
		XMFLOAT4 amb = XMFLOAT4(diffSpecCol.x/5.0f, diffSpecCol.y/5.0f, 
			diffSpecCol.z/5.0f, 1.0f);
		//Random location.
		XMFLOAT3 randLoc = XMFLOAT3(MathHelper::RandF(0.0f, 40.0f),
			MathHelper::RandF(1.0f, 10.0f), MathHelper::RandF(-5.0f, 55.0f));

		//Randon range.
		float randRange = MathHelper::RandF(1.0f, 5.0f);
		//Create point lights. 
		lightArr[i].InitPointLight(amb, diffSpecCol, diffSpecCol, 
			randLoc, randRange);

		//One spot light above the globe.
		//if (i == NUM_LIGHTS - 1)
		//	lightArr[i].InitSpotLight(null, 
		//		XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f), 
		//		XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f), 
		//		XMFLOAT3(20.0f, 15.0f, 0.0f), 20.0f, 
		//		XMFLOAT3(0, -1.0f, 0),
		//		10.0f);
	}

	//Random colour for the fun of it. Locations are NOT random (srand(0))
	srand((unsigned int)time(0));
	for (UINT i = 0; i < NUM_LIGHTS; i++)
	{
		//Random diffuse and spec colour.
		XMFLOAT4 diffCol = XMFLOAT4(MathHelper::RandF(0, 1), MathHelper::RandF(0,1), 
			MathHelper::RandF(0,1), 1.0f);
		lightArr[i].Diffuse = diffCol;
		lightArr[i].Specular = diffCol;
		lightArr[i].Specular.w = 64.0f;
	}

	//Set scene lighting - creating our required buffers - we want the option
	//to visualise our lights, so set the required bool to true.
	sharedGraphicsInterface->SetSceneLights(&lightArr[0], NUM_LIGHTS, true);

	//Delete dynamically allocated light array.
	delete []lightArr;

	//Set Directional light. 
	dLight.Ambient = XMFLOAT4(0.0768f, 0.0682f, 0.0596f, 1.0f);
	dLight.Diffuse = XMFLOAT4(0.768f, 0.682f, 0.596f, 1.0f);
	dLight.Specular = XMFLOAT4(0.768f, 0.682f, 0.596f, 64.0f);
	dLight.Direction = XMFLOAT3(+0.135f, -0.3f, +1.0f);

	//Set single directional light 
	sharedGraphicsInterface->SetDirectionalLights(&dLight, 1, 0);

	//Initial Scene State is to draw scene as usual.
	sceneState = SCENE_STATE_DRAW_SCENE;
	//Lights initially to be rendered
	shouldRenderLights = true;
	//Animate model. 
	shouldRenderAnimModRigged = false;

	sharedGraphicsInterface->SetHemisphericAmbientColours(XMFLOAT3(0.0f, 0.0f, 0.0f), 
		XMFLOAT3(0.298f, 0.4078f, 0.4745f));

	//Load fonts. 
	font = new FontSheet();
	spriteBatch = new SpriteBatch();

	HR(font->Initialize(sharedGraphicsInterface->GetDevice(), 
		L"Times New Roman", 96.0f, FontSheet::FontStyleItalic, true));
	HR(spriteBatch->Initialize(sharedGraphicsInterface->GetDevice()));

	//Load 2D texture
	HR(D3DX11CreateShaderResourceViewFromFile(sharedGraphicsInterface->GetDevice(), 
		L"Textures/stones.dds", 0, 0, &tex2D,0));

	//Create advanced models
	ocean = new OceanAdvModel();
	ocean->Init(50, 50, XMFLOAT3(15.0f, 5.0f, 60.0f), sharedGraphicsInterface->GetDevice());

	inlandWater = new InlandWaterAdvModel();
	inlandWater->Init(50, 50, XMFLOAT3(15.0f, 10.0f, 15.f), sharedGraphicsInterface->GetDevice());
}

void TestScene::CloseScene()
{
	SafeDelete(mod);
	SafeDelete(objMod);
	SafeDelete(asimpMod);
	SafeDelete(loadedMod);
	SafeDelete(teapotMod);

	SafeDelete(iMod);
	SafeDelete(iObjMod);
	SafeDelete(iAsimpMod);

	SafeDelete(animMod);
	
	SafeDelete(skyDome);

	SafeDelete(font);
	SafeDelete(spriteBatch);
	ReleaseCOM(tex2D);

	SafeDelete(ocean);
	SafeDelete(inlandWater);

	SafeDelete(terrain);
}

void TestScene::OnMouseDown(WPARAM btnState, int x, int y, UINT originalMessage)
{
	AbstractScene::OnMouseDown(btnState, x, y, originalMessage);
}

void TestScene::OnMouseUp(WPARAM btnState, int x, int y, UINT originalMessage)
{
	if (originalMessage == WM_RBUTTONUP)
		shouldRenderAnimModRigged = !shouldRenderAnimModRigged;
}

void TestScene::OnMouseMove(WPARAM btnState, int x, int y)
{
	AbstractScene::OnMouseMove(btnState, x, y);
}

void TestScene::OnResize()
{
	AbstractScene::OnResize();
}

void TestScene::UpdateWithDelta(float delta)
{
	AbstractScene::UpdateWithDelta(delta);

	//Decide what to render?
	if( GetAsyncKeyState('1') & 0x8000 )
		sceneState = SCENE_STATE_DRAW_SCENE; 
	if( GetAsyncKeyState('2') & 0x8000 )
		sceneState = SCENE_STATE_DRAW_Z_PRE_PASS_DEPTH_BUFFER; 
	if ( GetAsyncKeyState('3') & 0x8000 )
		sceneState = SCENE_STATE_DRAW_CS_MAX_Z_DEPTH_BUFFER;
	if ( GetAsyncKeyState('4') & 0x8000 )
		sceneState = SCENE_STATE_DRAW_CS_LLSEB_GREYSCALE;
	if ( GetAsyncKeyState('5') & 0x8000 )
		sceneState = SCENE_STATE_DRAW_CS_LLSEB_COLOUR;
	if ( GetAsyncKeyState('6') & 0x8000 )
		sceneState = SCENE_STATE_DRAW_CS_LLSEBT_GREYSCALE;
	if ( GetAsyncKeyState('7') & 0x8000 )
		sceneState = SCENE_STATE_DRAW_CS_LLSEBT_COLOUR;
	if ( GetAsyncKeyState('8') & 0x8000 )
		sceneState = SCENE_STATE_DRAW_SSAO_VIEW_NORMALS;
	if ( GetAsyncKeyState('9') & 0x8000 )
		sceneState = SCENE_STATE_DRAW_SSAO_VIEW_DEPTHS;
	if ( GetAsyncKeyState('0') & 0x8000 )
		sceneState = SCENE_STATE_DRAW_SSAO_MAP;
	if ( GetAsyncKeyState('L') & 0x8000 )
		sceneState = SCENE_STATE_DRAW_SHADOW_DEPTH;

	//Render the lights?
	if ( GetAsyncKeyState('G') & 0x8000 )
		shouldRenderLights = true;
	if ( GetAsyncKeyState('F') & 0x8000 )
		shouldRenderLights = false;

	//Update single directional light 
	sharedGraphicsInterface->SetDirectionalLights(&dLight, 1, 0);

	//Update animating models - loop. 
	//animMod->Update(delta, true, shouldRenderAnimModRigged);

	//Find Y location of the terrain at camera/player location
	XMFLOAT3 newPos = camera->GetPosition();
	newPos.y = terrain->GetHeight(newPos.x, newPos.z) + 5.0f;
	camera->SetPosition(newPos);
	camera->RebuildViewMatrix();

	//Update ocean
	//ocean->Update(delta);
	//inlandWater->Update(delta);
}

void TestScene::RenderScene()
{
	//
	//Start rendering - Clear backbuffer and depth stencil view.
	//
	float clearColGrey[4] = {0.6f, 0.6f, 0.6f, 1.0f};
	sharedGraphicsInterface->StartFrame(clearColGrey, camera);

	//
	//Add models to the batches. 
	//
	sharedGraphicsInterface->AddModelToBatch(mod);
	//sharedGraphicsInterface->AddModelToBatch(objMod);
	//sharedGraphicsInterface->AddModelToBatch(asimpMod);
	//sharedGraphicsInterface->AddModelToBatch(loadedMod);
	//sharedGraphicsInterface->AddModelToBatch(teapotMod);

	//sharedGraphicsInterface->AddModelToBatch(iMod);
	//sharedGraphicsInterface->AddModelToBatch(iObjMod);
	//sharedGraphicsInterface->AddModelToBatch(iAsimpMod);
	
	//sharedGraphicsInterface->AddModelToBatch(animMod);
	
	//Adv models
	//sharedGraphicsInterface->AddModelToBatch((AbstractAdvancedModel*)ocean);
	//sharedGraphicsInterface->AddModelToBatch((AbstractAdvancedModel*)inlandWater);
	sharedGraphicsInterface->AddModelToBatch((AbstractAdvancedModel*)terrain);

	//
	//Render the scene.
	//
	sharedGraphicsInterface->Render(shouldRenderLights);


#ifdef RENDER_2D_SPRITES
	//
	//2D Rendering
	//
	sharedGraphicsInterface->Begin2DRendering();

	//Draw 2D texture. 
	spriteBatch->BeginBatch(tex2D);
	spriteBatch->AddSpriteToBatch(sharedGraphicsInterface, 0.0f, 0.0f, 0.2f, 0.2f);
	spriteBatch->EndBatch(sharedGraphicsInterface->GetContext());

	// Center the text in the screen.
	spriteBatch->DrawStringUnorm(sharedGraphicsInterface, *font, 
		std::wstring(L"Hello World"), 0.5f, 0.5f);

	//End of text rendering
	sharedGraphicsInterface->End2DRendering();
#endif

	//
	//Debug Rendering
	//
	//Draw depth buffer (filled from Z Pre Pass).
	if (sceneState == SCENE_STATE_DRAW_Z_PRE_PASS_DEPTH_BUFFER)
		sharedGraphicsInterface->DrawZPrePassFilledDepthBuffer();
	//Draw max tile depth texture.
	else if (sceneState == SCENE_STATE_DRAW_CS_MAX_Z_DEPTH_BUFFER)
		sharedGraphicsInterface->DrawTileMaxZBuffer(); 
	else if (sceneState == SCENE_STATE_DRAW_CS_LLSEB_GREYSCALE)
		sharedGraphicsInterface->DrawLightListStartEndBufferGreyscale();
	else if (sceneState == SCENE_STATE_DRAW_CS_LLSEB_COLOUR)
		sharedGraphicsInterface->DrawLightListStartEndBufferColour();
	else if (sceneState == SCENE_STATE_DRAW_CS_LLSEBT_GREYSCALE)
		sharedGraphicsInterface->DrawLightListStartEndBufferTransparantGreyscale();
	else if (sceneState == SCENE_STATE_DRAW_CS_LLSEBT_COLOUR)
		sharedGraphicsInterface->DrawLightListStartEndBufferTransparantColour();
	else if (sceneState == SCENE_STATE_DRAW_SSAO_VIEW_NORMALS)
		sharedGraphicsInterface->DrawSSAOViewNormals();
	else if (sceneState == SCENE_STATE_DRAW_SSAO_VIEW_DEPTHS)
		sharedGraphicsInterface->DrawSSAOViewDepths();
	else if (sceneState == SCENE_STATE_DRAW_SSAO_MAP)
		sharedGraphicsInterface->DrawSSAOMap();
	else if (sceneState == SCENE_STATE_DRAW_SHADOW_DEPTH)
		sharedGraphicsInterface->DrawShadowMapDepth();

	//Present the backbuffer (if in full screen, present with vsync on)
	bool fsState = sharedGraphicsInterface->IsInFullScreenMode();
	sharedGraphicsInterface->EndFrame(fsState);
}